
/*
 * 在指定的中断描述符表中的位置 gate_addr 处，设置 8 字节的段描述符内容
 * 相当于按照如下顺序执行：
 *  %eax = 0x00080000
 *  %edx = addr（也就是中断程序的入口地址）
 *  %ax = %dx		// 相当于把中断入口程序的低16位存放到 %ax 中
 * 	%dx = 0x8000 + (dpl<<13)+(type<<8) // 这是设置 中断段描述符的第4/5字节
 * 			// P = 1，DPL=dpl, TYPE=type
 * 	经过上述操作后：
 * 	%eax 存放的是： 0x0008 中断入口程序地址的低16位
 * 			// 0x0008 是内核代码段的GDT段选择符
 *  %edx 存放的是： 中断入口程序地址的高16位 P = 1，DPL=dpl, TYPE=type
 *  然后把这8个字节存放到中断描述符表对应的位置
 *  [gate_addr] = %eax			// 低4字节
 *  [gate_addr + 4] = %edx		// 高4字节
 */
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
	"movw %0,%%dx\n\t" \
	"movl %%eax,%1\n\t" \
	"movl %%edx,%2" \
	: \
	: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
	"o" (*((char *) (gate_addr))), \
	"o" (*(4+(char *) (gate_addr))), \
	"d" ((char *) (addr)),"a" (0x00080000))

/*
 * 设置中断门的段描述符内容
 * 其中 &idt[n] 指向第 n 号中断向量对应的中断描述符地址， idt 是在 head.s 中定义 (_idt)
 * 中断门的类型是 0b1110 = 14
 * DPL = 0，特权级0
 */
#define set_intr_gate(n,addr) \
	_set_gate(&idt[n],14,0,addr)

/*
 * 设置陷阱门的段描述符内容
 * 其中 idt[n] 指向第 n 号中断向量对应的中断描述符地址， idt 是在 head.s 中定义 (_idt)
 * 陷阱门的类型是 0b1111 = 15
 * DPL = 0，特权级0
 */
#define set_trap_gate(n,addr) \
	_set_gate(&idt[n],15,0,addr)

/*
 * 设置陷阱门的段描述符内容
 * 其中 idt[n] 指向第 n 号中断向量对应的中断描述符地址， idt 是在 head.s 中定义 (_idt)
 * 陷阱门的类型是 0b1111 = 15
 * DPL = 3，特权级3，意味着用户态特权级3的程序可以通过此陷阱门段选择符，进入到内核态
 * 因此，这个宏也是作为系统调用的中断向量设置
 * 备注：陷阱门与中断门的区别，陷阱门在进入中断处理程序的时候不会自动清除CFLAGS的IF位（也就是可以被其他中断嵌套）
 */
#define set_system_gate(n,addr) \
	_set_gate(&idt[n],15,3,addr)


/*
 * 设置 tss ldt 段描述符
 * n --- 指向待设置的描述符的首地址，如果以字节算，设置n开始的8个字节信息
 * 		*n 表示 n 指向的地址
 * addr --- 描述符中的基地址
 * type --- 描述符类型
 * 	注意 "m" (var) 表示将变量 var 的地址作为操作数存起来，后续编译器针对的是该内存地址
 * 	因此，如果 var 本身是指针，需要解引用，这样会把该指针指向的地址作为操作数，而不是指针本身的地址
 * 代码逻辑如下
 movw $addr, %eax		# 描述符中的段基地址，指向 一个 tss 或 ldt 结构
 movw $104, (*n)		# 104=0x68，也就是段界限是 0x68，放入描述符 byte0-byte1
 movw %ax, (*(n+2))		# 段基址低16位，放入描述符 byte2-byte3
 rorl $16, %eax			# 段基址右移16位，也就是把段基址的高16位移到%ax
 movb %al, (*(n+4))		# 段基址的16-23位放入描述符 byte4
 movb $type, (*(n+5))	# 描述符类型放入到 byte5(0-3 TYPE，4 S，DPL 5-6，P 7)
 movb $0x00, (*(n+6))	# 段界限高4位，置为0，对应byte6[0-3]
 movb %ah, (*(n+7))		# 段基址的24-31位放入描述符byte7
 rorl $16, %eax			# 相当于 %eax = 0
 */
#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
	"movw %%ax,%2\n\t" \
	"rorl $16,%%eax\n\t" \
	"movb %%al,%3\n\t" \
	"movb $" type ",%4\n\t" \
	"movb $0x00,%5\n\t" \
	"movb %%ah,%6\n\t" \
	"rorl $16,%%eax" \
	::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
	 "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
	)

/**
 * tss的 type = 0x89 = 0b1000 1001 , byte5(0-3 TYPE，4 S，DPL 5-6，P 7)
 * 因此，其 TYPE = 0x1001 可用32位TSS，S = 0 系统段/门描述符
 * 			DPL = 0 内核态特权级，P = 1 段在内存中存在
 * */
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")

/**
 * ldt的 type = 0x82 = 0b1000 0010 , byte5(0-3 TYPE，4 S，DPL 5-6，P 7)
 * 因此，其 TYPE = 0x0010 为LDT，S = 0 系统段/门描述符
 * 			DPL = 0 内核态特权级，P = 1 段在内存中存在
 * */
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")
